﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FySystem.AssistantServices
{
    /// <summary>
    /// 异步内存工作队列
	/// 使用方式：
	/// serviceContext.AddService<WorkQueue>("alarmdb").Configure(service =>
    ///     {
    ///         AlarmDbService.ALARMDBSERVICE = (WorkQueue)service;
    ///         AlarmDbService.ALARMDBSERVICE.SetWorkDataCallback(AlarmDbService.DataCallback);
    ///     });
    /// </summary>
    public class WorkQueue:IAssistantService
    {
        Action<IAssistantService> _configFun;

        public void Configure(Action<IAssistantService> configFun)
        {
            _configFun = configFun;
        }

        public void Execute(IAssistantServiceContext assistantServiceContext, IAssistantService service)
        {
            try
            {
                Task task = null;
                if (WorkDatasCallback != null)
                {
                    task = new Task(RunList);
                }
                else if (WorkDataCallback != null)
                {
                    task = new Task(RunSingle);
                }
                task.Start();
            }
            catch (Exception ex)
            {
                FyLogContext.Write(ex.Message);
            }
        }

        public void InvokeConfigure()
        {
            _configFun?.Invoke(this);
        }

        /*********************************************************************************/
        /// <summary>
        /// 调用Dequeues函数时，最多一次获取的数据条数
        /// </summary>
        /// <param name="maxCnt"></param>
        /// <returns></returns>
        public WorkQueue SetMaxDequeueCount(int maxCnt)
        {
            MaxDequeueCount = maxCnt;
            return this;
        }

        /// <summary>
        /// 设置数据处理回调（单数据）
        /// </summary>
        /// <param name="callback"></param>
        /// <returns></returns>
        public WorkQueue SetWorkDataCallback(Action<object> callback)
        {
            WorkDataCallback = callback;
            return this;
        }

        /// <summary>
        /// 设置数据处理回调（多数据，优先）
        /// </summary>
        /// <param name="callback"></param>
        /// <returns></returns>
        public WorkQueue SetWorkDatasCallback(Action<List<object>> callback)
        {
            WorkDatasCallback = callback;
            return this;
        }
        /*********************************************************************************/

        public int MaxDequeueCount { get; private set; } = 100;

        public Action<object> WorkDataCallback { get; private set; }

        public Action<List<object>> WorkDatasCallback { get; private set; }
        /*********************************************************************************/

        private Queue<object> _queue = new Queue<object>();
        private ManualResetEvent _allDone = new ManualResetEvent(false);

        /// <summary>
        /// 放入队列
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        public void Enqueue<T>(T data)
        {
            try
            {
                if (data == null)
                    return;

                lock (_queue)
                {
                    _queue.Enqueue(data);
                }
                _allDone.Set();
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 放入队列
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="datas"></param>
        public void Enqueue<T>(List<T> datas)
        {
            try
            {
                if (datas == null)
                    return;
                if (datas.Count == 0)
                    return;

                lock (_queue)
                {
                    foreach (var item in datas)
                        _queue.Enqueue(item);
                }
                _allDone.Set();
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 单条数据获取
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public object Dequeue()
        {
            object res = null;
            try
            {
                lock (_queue)
                {
                    if (_queue.Count > 0)
                    {
                        res = _queue.Dequeue();
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }

        /// <summary>
        /// 多条数据获取
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public List<object> Dequeues()
        {
            List<object> res = null;
            try
            {
                lock (_queue)
                {
                    if (_queue.Count > 0)
                    {
                        res = new List<object>();
                        int cnt = _queue.Count >= MaxDequeueCount ? MaxDequeueCount : _queue.Count;
                        for (int i = 0; i < cnt; i++)
                            res.Add(_queue.Dequeue());
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }

        private void RunSingle()
        {
            while (true)
            {
                try
                {
                    _allDone.Reset();
                    object data = Dequeue();
                    if (data == null)
                    {
                        _allDone.WaitOne();
                        continue;
                    }

                    WorkDataCallback.Invoke(data);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }

        private void RunList()
        {
            while (true)
            {
                try
                {
                    _allDone.Reset();
                    List<object> datas = Dequeues();
                    if (datas == null)
                    {
                        _allDone.WaitOne();
                        continue;
                    }

                    WorkDatasCallback(datas);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }
    }
}
